package game;

import game.efekti.EffectFactory;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Vector;

import util.INIFile;
import util.INIGroup;
import util.ListFile;
import util.ListGroup;
import util.Util;
import GUI.ButtonSkin;
import GUI.ImageStorage;
import GUI.anim.Animation;


public class GameBase {
	private static GameBase instance = null;
	public static final String DATA_FOLDER = "Data" + File.separator ;
	
	private Vector<Item> baseItems = new Vector<Item>();
	private HashMap<String, Item> hmItems = new HashMap<String, Item>();
	
	private Vector<Character> baseChars = new Vector<Character>();
	private HashMap<String, Character> hmChars = new HashMap<String, Character>();
	
	private Vector<Tile> baseTiles = new Vector<Tile>();
	private HashMap<String, Tile> hmTiles = new HashMap<String, Tile>();
	
	private Vector<TileSet> baseSets = new Vector<TileSet>();
	private HashMap<String, TileSet> hmSets = new HashMap<String, TileSet>();
	
	private Vector<MagicEffect> baseEffects = new Vector<MagicEffect>();
	private HashMap<String, MagicEffect> hmEffects = new HashMap<String, MagicEffect>();
	
	private HashMap<String, Vector<String> > lists = new HashMap<String, Vector<String>>();
	private HashMap<String, String> langMap = new HashMap<String, String>();
	
	private Vector<TileMap> baseMaps = new Vector<TileMap>();
	private HashMap<String, TileMap> hmMaps = new HashMap<String, TileMap>();
	
	private Vector<Spell> baseSpells = new Vector<Spell>();
	private HashMap<String, Spell> hmSpells = new HashMap<String, Spell>();
	
	private Vector<Portal> basePortals = new Vector<Portal>();
	private HashMap<String, Portal> hmPortals = new HashMap<String, Portal>();
	
	private Vector<Animation> baseAnims = new Vector<Animation>();
	private HashMap<String, Animation> hmAnims = new HashMap<String, Animation>();
	
	private HashMap<String, BufferedImage> baseImages = new HashMap<String, BufferedImage>();
	
	private Vector<INIGroup> cInstances = new Vector<INIGroup>();
	private Vector<INIGroup> iInstances = new Vector<INIGroup>();
	
	private static boolean dataLoaded = false;
	private static boolean dataInit = false;
	
	public static GameBase get()
	{
		if(instance == null) { instance = new GameBase(); instance.initData(); }
		return instance;
	}
	
	public GameBase()
	{
		loadData();
	}
	
	public void instantiateItems() throws CloneNotSupportedException, Exception
	{
		for(INIGroup g : iInstances)
		{
			TileMap map = getMapByTag(g.getString("Map"));
			if(map == null)
			{
				System.out.println("Map not found : " + g.getString("Map"));
				continue;
			}
			
			int genLevel = g.getInteger("GenLevel");
			if(genLevel > 0)
			{
				Item temp = Item.genItem(genLevel);
				map.addItem(temp, g.getInteger("PosX"), g.getInteger("PosY"));
			}
			else
			{
				Item temp = getItemByTag(g.getString("Item"));
				if(temp == null)
				{
					System.out.println("Item not found : " + g.getString("Item"));
					continue;
				}
				
				map.addItem(temp.clone(), g.getInteger("PosX"), g.getInteger("PosY"));
			}
		}
	}
	
	public void instantiateCharacters() throws CloneNotSupportedException
	{
		for(INIGroup g : cInstances)
		{
			TileMap map = getMapByTag(g.getString("Map"));
			if(map == null)
			{
				System.out.println("Map not found : " + g.getString("Map"));
				continue;
			}
			
			Character temp = getCharacterByTag(g.getString("Character"));
			if(temp == null)
			{
				System.out.println("Character not found : " + g.getString("Character"));
				continue;
			}
			
			Character inst = temp.Clone();
			Item cWeapon = getItemByTag(g.getString("ItmWeapon"));
			Item cShield = getItemByTag(g.getString("ItmShield"));
			Item cArmour = getItemByTag(g.getString("ItmArmour"));
			Item cHelmet = getItemByTag(g.getString("ItmHelmet"));
			
			Item misc1 = getItemByTag(g.getString("ItmMisc1"));
			Item misc2 = getItemByTag(g.getString("ItmMisc2"));
			Item misc3 = getItemByTag(g.getString("ItmMisc3"));
			
			if(cWeapon != null)
			{
				cWeapon = cWeapon.clone();
				inst.AddToInventory(cWeapon);
				inst.EquipItem(cWeapon);
			}
			
			if(cShield != null)
			{
				cShield = cShield.clone();
				inst.AddToInventory(cShield);
				inst.EquipItem(cShield);
			}
			
			if(cArmour != null)
			{
				cArmour = cArmour.clone();
				inst.AddToInventory(cArmour);
				inst.EquipItem(cArmour);
			}
			
			if(cHelmet != null)
			{
				cHelmet = cHelmet.clone();
				inst.AddToInventory(cHelmet);
				inst.EquipItem(cHelmet);
			}
			
			if(misc1 != null) inst.AddToInventory(misc1.clone());
			if(misc2 != null) inst.AddToInventory(misc2.clone());
			if(misc3 != null) inst.AddToInventory(misc3.clone());
			
			int genLvl = g.getInteger("GenItemLevel");
			if(genLvl > 0)
			{
				inst.AddToInventory(Item.genItem(genLvl));
			}
			
			inst.setGold(Util.getRndInt(g.getInteger("GoldMin"), g.getInteger("GoldMax")));
			
			try {
				map.addCharacter(inst, g.getInteger("PosX"), g.getInteger("PosY"));
			} catch (Exception e) { System.out.println("IT PHAILS!"); }
		}
	}
	
	public void addAsset(Loadable a)
	{
		if(a == null) return;
		Loadable test = getAsset(a.getTag());
		if(test != null)
		{
			if(test.getClass() == a.getClass())
			{
				System.out.println("Error, duplicate tag in the same category : " + a.getTag() + 
						" (" + a.getClass().getName() + ")");
				return;
			}
			else
			{
				System.out.println("Warning, duplicate tag across " + a.getClass().getName() + 
						" and " + test.getClass().getName() + " categories : " + a.getTag());
			}
		}
		
		if(a instanceof Character)
		{
			baseChars.add((Character) a);
			hmChars.put(a.getTag(), (Character)a);
		} else if (a instanceof Item)
		{
			baseItems.add((Item) a);
			hmItems.put(a.getTag(), (Item)a);
		} else if (a instanceof TileMap)
		{
			baseMaps.add((TileMap) a);
			hmMaps.put(a.getTag(), (TileMap)a);
		} else if (a instanceof Tile)
		{
			baseTiles.add((Tile) a);
			hmTiles.put(a.getTag(), (Tile)a);
		} else if (a instanceof TileSet)
		{
			baseSets.add((TileSet) a);
			hmSets.put(a.getTag(), (TileSet)a);
		} else if (a instanceof MagicEffect)
		{
			baseEffects.add((MagicEffect) a);
			hmEffects.put(a.getTag(), (MagicEffect)a);
		} else if (a instanceof Spell)
		{
			baseSpells.add((Spell) a);
			hmSpells.put(a.getTag(), (Spell)a);
		} else if (a instanceof Portal)
		{
			basePortals.add((Portal) a);
			hmPortals.put(a.getTag(), (Portal)a);
		} else if (a instanceof Animation)
		{
			baseAnims.add((Animation) a);
			hmAnims.put(a.getTag(), (Animation)a);
		}
		else
		{
			System.out.println("Unknown asset : " + a);
		}
	}
	
	public Loadable getAsset(String tag)
	{
		Character c = NSgetCharacterByTag(tag);
		if(c != null) return c;
		Item i = NSgetItemByTag(tag);
		if(i != null) return i;
		Portal p = NSgetPortalByTag(tag);
		if(p != null) return p;
		Tile t = NSgetTileByTag(tag);
		if(t != null) return t;
		TileMap tm = NSgetMapByTag(tag);
		if(tm != null) return tm;
		Spell s = NSgetSpellByTag(tag);
		if(s != null) return s;
		MagicEffect me = NSgetEffectByTag(tag);
		if(me != null) return me;
		TileSet ts = NSgetTileSetByTag(tag);
		if(ts != null) return ts;
		Animation anim = NSgetAnimationByTag(tag);
		if(anim != null) return anim;
		
		return null;
	}
	
	public void loadData()
	{
		if(dataLoaded) return;
		File df = new File(DATA_FOLDER);
		File[] flist = df.listFiles();
		
		if(flist == null)
		{
			System.out.println("No data files found, epic failure");
			try {
				System.out.println("Current path : " + new File(".").getCanonicalPath());
			} catch (IOException e) {}
			return;
		}
		
		System.out.println("GameBase init, loading data files...");
		
		for(File f : flist)
		{
			if((f.getName().endsWith(".txt") || f.getName().endsWith(".dat")))
			{
				INIFile ini;
				try {
					ini = new INIFile(f);
					INIGroup g = null;
					while((g = ini.getNext()) != null)
					{
						if("Character".equals(g.title))
						{
							Character nc = new Character();
							nc.loadFromFile(g);
							//System.out.println("LC : " + nc.getTag());
							addAsset(nc);
						}
						else if("Item".equals(g.title))
						{
							Item ni = new Item();
							ni.loadFromFile(g);
							addAsset(ni);
						}
						else if("Map".equals(g.title))
						{
							TileMap m = new TileMap();
							m.loadFromFile(g);
							addAsset(m);
						}
						else if("Spell".equals(g.title))
						{
							Spell s = new Spell();
							s.loadFromFile(g);
							addAsset(s);
						}
						else if("Tile".equals(g.title))
						{
							Tile t = new Tile();
							t.loadFromFile(g);
							addAsset(t);
						}
						else if("TileSet".equals(g.title))
						{
							TileSet ts = new TileSet();
							ts.loadFromFile(g);
							addAsset(ts);
						}
						else if("MagicEffect".equals(g.title))
						{
							MagicEffect me = EffectFactory.loadFromFile(g);
							addAsset(me);
						}
						else if("Portal".equals(g.title))
						{
							Portal por = new Portal();
							por.loadFromFile(g);
							addAsset(por);
						}
						else if("Animation".equals(g.title))
						{
							Animation anim = new Animation();
							anim.loadFromFile(g);
							addAsset(anim);
						}
						else if("CharacterInstance".equals(g.title))
						{
							cInstances.add(g);
						}
						else if("ItemInstance".equals(g.title))
						{
							iInstances.add(g);
						}
						else if("LangStrings".equals(g.title))
						{
							g.mergeTo(langMap);
						}
						else if("ButtonSkin".equals(g.title))
						{
							ButtonSkin.loadToBase(g);
						}
					}
				} 
				catch (Exception e) 
				{
					System.out.println("Failed to load data file : " + f + "(" + e + ")");
					e.printStackTrace();
				}
			}
			else if(f.getName().endsWith(".lst"))
			{
				try {
					ListFile lst = new ListFile(f);
					ListGroup g = null;
					
					while((g = lst.getNext()) != null)
					{
						Vector<String> str = lists.get(g.title);
						if(str == null)
						{
							str = new Vector<String>();
							lists.put(g.title, str);
						}
						
						str.addAll(g.getVector());
					}
				} catch (Exception e) {
					System.out.println("Unable to load list");
				}
				
			}
		}
		
		if(baseChars.size() > 0) System.out.println("Loaded " + baseChars.size() + " character(s)");
		if(baseItems.size() > 0) System.out.println("Loaded " + baseItems.size() + " item(s)");
		if(baseTiles.size() > 0) System.out.println("Loaded " + baseTiles.size() + " tile(s)");
		if(baseSets.size() > 0) System.out.println("Loaded " + baseSets.size() + " tileset(s)");
		if(baseEffects.size() > 0) System.out.println("Loaded " + baseEffects.size() + " magic effect(s)");
		if(baseMaps.size() > 0) System.out.println("Loaded " + baseMaps.size() + " map(s)");
		if(baseSpells.size() > 0) System.out.println("Loaded " + baseSpells.size() + " spell(s)");
		if(basePortals.size() > 0) System.out.println("Loaded " + basePortals.size() + " portal(s)");
		if(baseAnims.size() > 0) System.out.println("Loaded " + baseAnims.size() + " animations(s)");
		
		dataLoaded = true;
	}
	
	public static String getRndFromList(String listName)
	{
		Vector<String> lst = get().lists.get(listName);
		if(lst == null) return null;
		return lst.get(Util.getRndInt(lst.size()));
	}
	
	public static Vector<String> getList(String listName)
	{
		return get().lists.get(listName);
	}
	
	public void initData()
	{
		if(dataInit) return;
		dataInit = true;
		
		System.out.println("Init... ");
		
		for(Character c : baseChars)
			c.init();
		for(Item i : baseItems)
			i.init();
		for(Tile t : baseTiles)
			t.init();
		for(TileSet ts : baseSets)
			ts.init();
		for(TileMap tm : baseMaps)
			tm.init();
		for(Spell s : baseSpells)
			s.init();
		for(Portal p : basePortals)
			p.init();
		for(Animation a : baseAnims)
			a.init();
		
		if(getList("GUIImages") != null)
		{
			for(String s : getList("GUIImages"))
			{
				BufferedImage img = ImageStorage.getImage("GUI" + File.separator + s + ".png");
				if(img != null)
					baseImages.put(s, img);
			}
		}
		
		ButtonSkin.initButtons();
		
		try {
			instantiateItems();
			instantiateCharacters();
		}  
		catch (Exception e) {
			System.out.println("Instantiation failed.");
			e.printStackTrace();
		}
		
		System.out.println("Init done, GameBase ok");
	}
	
	public static BufferedImage getImageByTag(String tag) { return get().baseImages.get(tag); }
	public static Portal getPortalByTag(String tag) { return get().NSgetPortalByTag(tag); }
	protected static Item getItemByTag(String tag) { return get().NSgetItemByTag(tag); }
	protected static Character getCharacterByTag(String tag) { return get().NSgetCharacterByTag(tag); }
	public static Tile getTileByTag(String tag) { return get().NSgetTileByTag(tag); }
	public static TileSet getTileSetByTag(String tag) { return get().NSgetTileSetByTag(tag); }
	public static MagicEffect getEffectByTag(String tag) { return get().NSgetEffectByTag(tag); }
	public static TileMap getMapByTag(String tag) { return get().NSgetMapByTag(tag); }
	public static Spell getSpellByTag(String tag){ return get().NSgetSpellByTag(tag); }
	public static Animation getAnimationByTag(String tag){ return get().NSgetAnimationByTag(tag); }
	
	public static Item instItemByTag(String tag)
	{
		Item i = getItemByTag(tag);
		if(i == null) return null;
		try {
			return (Item) i.clone();
		} catch (CloneNotSupportedException e) {
			System.out.println("Item cloning failed");
			return null;
		}
	}
	
	public static Character instCharacterByTag(String tag)
	{
		Character c = getCharacterByTag(tag);
		if(c == null) return null;
		return (Character) c.clone();
	}
	
	public static String getString(String tag) 
	{
		String s = get().langMap.get(tag);
		if(s == null)
			return tag;
		else
			return s;
	}
	
	private Portal NSgetPortalByTag(String tag) { return hmPortals.get(tag); }
	private Item NSgetItemByTag(String tag) { return hmItems.get(tag); }
	private Character NSgetCharacterByTag(String tag) { return hmChars.get(tag); }
	private Tile NSgetTileByTag(String tag) { return hmTiles.get(tag); }
	private TileSet NSgetTileSetByTag(String tag) { return hmSets.get(tag); }
	private MagicEffect NSgetEffectByTag(String tag) { return hmEffects.get(tag); }
	private TileMap NSgetMapByTag(String tag) { return hmMaps.get(tag); }
	private Spell NSgetSpellByTag(String tag){ return hmSpells.get(tag); }
	private Animation NSgetAnimationByTag(String tag) { return hmAnims.get(tag); }

	public Vector<Item> getBaseItems() {
		return baseItems;
	}

	public Vector<Character> getBaseChars() {
		return baseChars;
	}
}
